﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using NUnit.Framework;
using OpenPowerPoint.Collection;

namespace OpenPowerPointTest.Collections
{
    [TestFixture]
    [Category("Unit: Notifyable List")]
    public class NotifyableList
    {
        private NotifierClass _baseItem;
        private NotifyableList<NotifierClass> _baseList;

        #region Setup/Teardown
        [SetUp]
        public void Setup()
        {
            _baseList = new NotifyableList<NotifierClass>();
            _baseItem = new NotifierClass() { Caption = "caption" };

            _baseList.Add(_baseItem);
        }

        [TearDown]
        public void Teardown()
        {

        }
        #endregion

        #region List add/remove child event handler hook
        [Test]
        public void ListConstructor()
        {
            //We have to clear the old list to avoid multiple items
            _baseList.Clear();
            _baseList = new NotifyableList<NotifierClass>(new[] { _baseItem });

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            Assert.IsNotNull(listeners, "No listeners registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListAdd()
        {
            _baseList.Clear();
            _baseList.Add(_baseItem);

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            Assert.IsNotNull(listeners, "No listeners registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListAddRange()
        {
            _baseList.Clear();
            _baseList.AddRange(new[] { _baseItem });

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            Assert.IsNotNull(listeners, "No listeners registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListInsert()
        {
            _baseList.Clear();
            _baseList.Insert(0, _baseItem);

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            Assert.IsNotNull(listeners, "No listeners registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListInsertRange()
        {
            _baseList.Clear();
            _baseList.InsertRange(0, new[] { _baseItem });

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            Assert.IsNotNull(listeners, "No listeners registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListRemove()
        {
            _baseList.Remove(_baseItem);

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            //for new tests
            _baseList.Add(_baseItem);

            Assert.IsNull(listeners, "Listeners remained registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListRemoveAt()
        {
            _baseList.RemoveAt(0);

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            //for new tests
            _baseList.Add(_baseItem);

            Assert.IsNull(listeners, "Listeners remained registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListRemoveAll()
        {
            _baseList.RemoveAll(@class => true);

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            //for new tests
            _baseList.Add(_baseItem);

            Assert.IsNull(listeners, "Listeners remained registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListRemoveRange()
        {
            _baseList.RemoveRange(0, 1);

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            //for new tests
            _baseList.Add(_baseItem);

            Assert.IsNull(listeners, "Listeners remained registered for event PropertyChanged of child item");
        }

        [Test]
        public void ListClear()
        {
            _baseList.Clear();

            var listeners =
                (typeof(NotifierClass).GetField("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Instance)
                                       .GetValue(_baseItem) as Delegate);

            //for new tests
            _baseList.Add(_baseItem);

            Assert.IsNull(listeners, "Listeners remained registered for event PropertyChanged of child item");
        }
        #endregion

        #region List Events
        [Test]
        public void EventAdd()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsAdd", e.Property, "Property not correctly set");
                                                 Assert.AreEqual(null, e.OldValue, "Old value was not null");
                                                 Assert.AreSame(_baseItem, e.NewValue, "New value not correctly set");
                                             };
            _baseList.Add(_baseItem);

            Assert.That(_event, Is.EqualTo(true), "Add Event not casted");
        }

        [Test]
        public void EventAddRange()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsAdd", e.Property, "Property not correctly set");
                                                 Assert.AreEqual(null, e.OldValue, "Old value was not null");
                                                 Assert.AreSame(_baseItem, e.NewValue, "New value not correctly set");
                                             };
            _baseList.AddRange(new[] { _baseItem });

            Assert.That(_event, Is.EqualTo(true), "AddRange Event not casted");
        }

        [Test]
        public void EventInsert()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsAdd", e.Property, "Property not correctly set");
                                                 Assert.AreEqual(null, e.OldValue, "Old value was not null");
                                                 Assert.AreSame(_baseItem, e.NewValue, "New value not correctly set");
                                             };
            _baseList.Insert(0, _baseItem);

            Assert.That(_event, Is.EqualTo(true), "Insert Event not casted");
        }

        [Test]
        public void EventInsertRange()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsAdd", e.Property, "Property not correctly set");
                                                 Assert.AreEqual(null, e.OldValue, "Old value was not null");
                                                 Assert.AreSame(_baseItem, e.NewValue, "New value not correctly set");
                                             };
            _baseList.InsertRange(0, new[] { _baseItem });

            Assert.That(_event, Is.EqualTo(true), "InsertRange Event not casted");
        }

        [Test]
        public void EventRemove()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsRemove", e.Property, "Property not correctly set");
                                                 Assert.AreSame(_baseItem, e.OldValue, "Old value not correctly set");
                                                 Assert.AreEqual(null, e.NewValue, "New value was not null");
                                             };
            _baseList.Remove(_baseItem);

            Assert.That(_event, Is.EqualTo(true), "Insert Event not casted");
        }

        [Test]
        public void EventRemoveAt()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsRemove", e.Property, "Property not correctly set");
                                                 Assert.AreSame(_baseItem, e.OldValue, "Old value not correctly set");
                                                 Assert.AreEqual(null, e.NewValue, "New value was not null");
                                             };
            _baseList.RemoveAt(0);

            Assert.That(_event, Is.EqualTo(true), "Insert Event not casted");
        }

        [Test]
        public void EventRemoveRange()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsRemove", e.Property, "Property not correctly set");
                                                 Assert.AreSame(_baseItem, e.OldValue, "Old value not correctly set");
                                                 Assert.AreEqual(null, e.NewValue, "New value was not null");
                                             };
            _baseList.RemoveRange(0, 1);

            Assert.That(_event, Is.EqualTo(true), "RemoveRange Event not casted");
        }

        [Test]
        public void EventRemoveAll()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 _event = true;
                                                 Assert.AreEqual("IsRemove", e.Property, "Property not correctly set");
                                                 Assert.AreSame(_baseItem, e.OldValue, "Old value not correctly set");
                                                 Assert.AreEqual(null, e.NewValue, "New value was not null");
                                             };
            _baseList.RemoveAll(@class => true);

            Assert.That(_event, Is.EqualTo(true), "RemoveAll Event not casted");
        }

        [Test]
        public void EventClear()
        {
            bool _event = false;

            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
            {
                _event = true;
                Assert.AreEqual("clear", e.Property, "Property not correctly set");
                Assert.AreEqual(null, e.NewValue, "New value was not null");
                Assert.IsInstanceOf<List<NotifierClass>>(e.OldValue, "Old value had wrong type");
                Assert.AreEqual(1, ((List<NotifierClass>)e.OldValue).Count, "Wrong number of items in OldValue");
                Assert.AreSame(_baseItem, ((List<NotifierClass>)e.OldValue)[0], "Item in OldValue was not the removed item");
            };
            _baseList.Clear();

            Assert.That(_event, Is.EqualTo(true), "Clear Event not casted");
        }
        #endregion

        #region Method abort tests
        [Test]
        public void AbortAdd()
        {
            _baseList.Clear();

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = e.OldValue;
                                             };
            _baseList.Add(_baseItem);

            Assert.AreEqual(0, _baseList.Count, "Canceling Add not working");
        }

        [Test]
        public void AbortAddRange()
        {
            _baseList.Clear();

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = e.OldValue;
                                             };
            _baseList.Add(_baseItem);

            Assert.AreEqual(0, _baseList.Count, "Canceling AddRange not working");
        }

        [Test]
        public void AbortInsert()
        {
            _baseList.Clear();

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = e.OldValue;
                                             };
            _baseList.Insert(0, _baseItem);

            Assert.AreEqual(0, _baseList.Count, "Canceling Insert not working");
        }

        [Test]
        public void AbortInsertRange()
        {
            _baseList.Clear();

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = e.OldValue;
                                             };
            _baseList.InsertRange(0, new[] { _baseItem });

            Assert.AreEqual(0, _baseList.Count, "Canceling InsertRange not working");
        }

        [Test]
        public void AbortRemove()
        {
            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = "";
                                             };
            _baseList.Remove(_baseItem);

            Assert.AreEqual(1, _baseList.Count, "Canceling Remove not working");
        }

        [Test]
        public void AbortRemoveAt()
        {
            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = "";
                                             };
            _baseList.RemoveAt(0);

            Assert.AreEqual(1, _baseList.Count, "Canceling RemoveAt not working");
        }

        [Test]
        public void AbortRemoveAll()
        {
            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = "";
                                             };
            _baseList.RemoveAll(@class => true);

            Assert.AreEqual(1, _baseList.Count, "Canceling RemoveAll not working");
        }

        [Test]
        public void AbortRemoveRange()
        {
            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = "";
                                             };
            _baseList.RemoveRange(0, 1);

            Assert.AreEqual(1, _baseList.Count, "Canceling RemoveRange not working");
        }

        [Test]
        public void AbortClear()
        {
            _baseList.Clear();
            _baseList.Add(_baseItem);

            _baseList.PropertyChanged += (o, e) =>
                                             {
                                                 e.NewValue = "";
                                             };
            _baseList.Clear();

            Assert.AreEqual(1, _baseList.Count, "Canceling Clear not working");
        }
        #endregion
    }
}
